#pragma once
#include<assert.h>
#include "FreeList.hpp"             
#include "CorrectionIndex.hpp"
#include "CentralCache.hpp"

namespace ThreadCacheModule
{
    using namespace FreeListModule;
    using namespace CorrectionIndexModule;
    using namespace CentralCacheModule;

    class ThreadCache
    {
    public:
        // 申请内存对象
        void* Allocate(size_t size)
        {
            assert(size < MAXMEMORYSIZE);

            // 算出对齐数
            size_t align_num = CorrectionIndex::AlignedNum(size);
            // 算出在哪个桶中
            size_t bucket_num = CorrectionIndex::BucketNum(size);

            // 开始申请内存
            if (!_freelist[bucket_num].Empty())
            {
                // 如果ThreadCache中存在这样大小的内存，就直接返回一个
                return _freelist[bucket_num].Pop();
            }
            else
            {
                // 说明当前ThreadCache中不存在这样大小的内存片，只能从下层的CentealCache中获取
                return FetchFromCentralCache(bucket_num, align_num);
            }
        }

        // 从CentralCache获取内存对象，size这里是已经对齐后的
        void* FetchFromCentralCache(size_t bucket_num, size_t size)
        {
            //使用慢调节算法
            size_t obtainednum = min(_freelist[bucket_num].Count(), CorrectionIndex::ObtainedNum(size));
            if (obtainednum == _freelist[bucket_num].Count())
                _freelist[bucket_num].Count()++;
            //从Central Cache获取多个内存块，使用obtainednum更新实际获取的内存块个数
            void* start = nullptr, * end = nullptr;
            obtainednum = CentralCache::GetInstance()->FetchRangeObj(start, end, obtainednum, size);
            if (obtainednum == 1)
            {
                assert(start == end);
                return start;
            }
            else if (obtainednum > 1)
            {
                //将多余的内存块用自由链表管理起来
                _freelist[bucket_num].Push(*((void**)start), end, obtainednum-1);
                return start;
            }
            else
            {
                assert(false);
                return nullptr;
            }
        }

        // 释放内存对象
        void Deallocate(void* ptr, size_t size)
        {
            assert(ptr);
            assert(size <= MAXMEMORYSIZE);

            // 直接将内存片放回到freelist中
            size_t bucket_num = CorrectionIndex::BucketNum(size);
            _freelist[bucket_num].Push(ptr);
            //当长度过大时就会释放回Central Cache
            if (_freelist[bucket_num].Size() >= _freelist[bucket_num].Count())
            {
                ListTooLong(_freelist[bucket_num], size);
            }
        }
        void ListTooLong(FreeList& freelist, size_t size)
        {
            void* begin = nullptr, * end = nullptr;
            //取得要被释放的区间长度
            freelist.Pop(begin, end, freelist.Count());
            CentralCache::GetInstance()->ReleaseListToSpans(begin, size);
        }
    private:
        FreeList _freelist[MAXBUCKETNUM];        // 自由列表桶
    };

    // 每个线程都要有对应的ThreadCache，这就需要使用到线程的局部性原理
    // 这里不在条件编译Linux或者Windows系统么认识使用C++11标准下定义的关键字
    thread_local ThreadCache* pTLSThreadCache = nullptr;
}